/*
 * Daniel R Padilla
 *
 * Copyright (c) 2008, Daniel R Padilla
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.databasefacade.select;


import java.util.HashMap;
import java.util.Iterator;

import org.databasefacade.select.functions.Function;
import org.databasefacade.select.functions.FunctionGlossary;
import org.databasefacade.table.Table;
import org.databasefacade.table.column.AliasedColumnList;
import org.databasefacade.table.column.Column;
import org.databasefacade.translator.Translator;


public class Select
{
	private boolean isDistinct = false;
	private AliasedColumnList selectColumnList;
	private FunctionList selectFunctionList;
	private FunctionGlossary functionGlossary = new FunctionGlossary(selectFunctionList);
	private HashMap constants = new HashMap();
	private HashMap calculations = new HashMap();
	private Translator translator;
	private QueryHints hints;
	
	public Select(Translator translator) {
		this.translator = translator;
		hints = translator.getQueryHints();
		selectFunctionList = new FunctionList(translator);
		selectColumnList = new AliasedColumnList(translator);
	}
	
	public QueryHints hints()
	{
		return hints;
	}
	
	public void allTableColumns(Table table)
	{
		Iterator columns = table.getColumnNames();
		while ( columns.hasNext() ) {
			String colName = columns.next().toString();
			Column col = table.getColumn(colName);
			col.setTableAlias(table.getTableAlias());
			column(col);
		}
	}

	public void column(Column col)
	{
		selectColumnList.addAliasedColumn(col);
	}

	public void column(Column col, String colAlias)
	{
		selectColumnList.addAliasedColumn(col.getCopy(), colAlias);
	}

	public void function(Function function, String colAlias)
	{
		selectFunctionList.addFunction(function, colAlias);
	}
	
	public void constant(String constant, String colAlias)
	{
		String d = translator.getStringDelimiter();
		constants.put(colAlias, d + constant + d);
	}
	
	public void calculation(Arithmetic calc, String colAlias)
	{
		constants.put(colAlias, calc.getSQL());
	}
	
	public FunctionGlossary functions()
	{
		return functionGlossary;
	}

	public void setDistinct(boolean isDistinct)
	{
		this.isDistinct = isDistinct;
	}

	public boolean getIsDistinct()
	{
		return isDistinct;
	}

	public String getSQL()
	{
		// Prepare distinct and hint strings
		String distinct = "";
		if ( isDistinct ) {
			distinct = "DISTINCT";
		}
		String hintSQL = translator.getSelectHintSQL(this.hints);
		if ( hintSQL == null ) {
			hintSQL = "";
		}
		
		// Assign select
		StringBuilder selectSQL = new StringBuilder("SELECT");
		
		// Assign distinct
		if ( distinct.length() > 0 ) {
			selectSQL.append(" " + distinct);
		}
		
		// Assign hint
		if ( hintSQL.length() > 0 ) {
			selectSQL.append(" " + hintSQL);
		}
		
		// Assign select column list
		if ( selectColumnList.size() > 0 ) {
			selectSQL.append(" " + selectColumnList.getSQL());
		}
		
		// Assign select function list
		if ( selectFunctionList.size() > 0 ) {
			if ( selectColumnList.size() > 0 ) {
				selectSQL.append("\n      ," + selectFunctionList.getSQL());
			} else {
				selectSQL.append(" " + selectFunctionList.getSQL());
			}
		}
		
		// Assign calculations
		if ( calculations.size() > 0 ) {
			if ( selectColumnList.size() > 0 || selectFunctionList.size() > 0 ) {
				selectSQL.append("\n      ," + getHashMapSQL(calculations));
			} else {
				selectSQL.append(" " + getHashMapSQL(calculations));
			}
		}
		
		// Assign constants
		if ( constants.size() > 0 ) {
			if ( selectColumnList.size() > 0 || selectFunctionList.size() > 0
				|| calculations.size() > 0 ) {
				selectSQL.append("\n      ," + getHashMapSQL(constants));
			} else {
				selectSQL.append(" " + getHashMapSQL(constants));
			}
		}
		
		return selectSQL.toString();
	}
	
	private String getHashMapSQL(HashMap sqlStrings)
	{
		StringBuilder sql = new StringBuilder();
		Iterator aliases = sqlStrings.keySet().iterator();
		String comma = "";
		while ( aliases.hasNext() ) {
			String alias = (String) aliases.next();
			sql.append(comma + sqlStrings.get(alias) + " " + alias);
			comma = "\n      ,";
		}
		return sql.toString();
	}
	
	public void assignGroupBy(GroupBy groupBy)
	{
		boolean hasAggregateFunction = false;
		if ( selectFunctionList.size() > 0 ) {
			for ( int i = 0; i < selectFunctionList.size(); i++ ) {
				Function func = selectFunctionList.getFunction(i);
				if ( func.isAggregate() ) {
					hasAggregateFunction = true;
					break;
				}
			}
		}
		
		if ( hasAggregateFunction ) {
			for (int c=0; c < selectColumnList.size(); c++) {
				Column col = selectColumnList.getColumn(c);
				groupBy.column(col);
			}
			
			/*
			for (int f=0; f < selectFunctionList.size(); f++) {
				Function func = selectFunctionList.getFunction(f);
			}
			*/
			
			Iterator keys = calculations.keySet().iterator();
			while ( keys.hasNext() ) {
				String calc = (String) calculations.get(keys.next());
				groupBy.calculation(calc);
			}
		}
	}
	
}
